package com.sinotn.ks.node.report;

import java.io.File;
import java.io.RandomAccessFile;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import com.sinotn.SinotnLogger;
import com.sinotn.SinotnRuntime;
import com.sinotn.error.MessageException;
import com.sinotn.gson.GsonFacade;
import com.sinotn.ks.definition.AbstractExamEvent;
import com.sinotn.ks.definition.ExamEngine;
import com.sinotn.ks.definition.Examinee;
import com.sinotn.ks.definition.Message;
import com.sinotn.ks.definition.MessageFile;
import com.sinotn.ks.netty.MessageNotifier;
import com.sinotn.ks.netty.message.DefaultMessageFile;
import com.sinotn.ks.netty.message.SimpleMessage;
import com.sinotn.ks.node.meta.service.ServerAuthService;
import com.sinotn.ks.node.runtime.FileStoreUtil;
import com.sinotn.ks.node.runtime.dao.cond.ExamCertifierCond;
import com.sinotn.ks.node.runtime.service.PlanService;
import com.sinotn.ks.node.runtime.service.RuntimeService;
import com.sinotn.ks.node.runtime.vo.ExamCertifierVO;
import com.sinotn.ks.thirdsys.api.platform.KsPlatform;
import com.sinotn.ks.thirdsys.api.platform.netty.NettyClient;

public class FaceSignReport extends AbstractExamEvent implements Runnable {
	@Autowired
	private ExamEngine examEngine;
	@Autowired
	private ServerAuthService serverAuthSvr;
	@Autowired
	private PlanService planService;
	@Autowired
	private RuntimeService runtimeSvr;
	@Autowired
	private KsPlatform ksPlatform;
	private NettyClient client;
	private int order=1;
	private boolean start=false;
	private int errCount=0;
	private long lazyMills=0;
	// 最大延时上报时间5分钟
	private long maxLazyMills=300000;
	// 默认5秒上报时间间隔
	private long reportInterval=5000;
	private String serverId=null;
	private Date reportTime=null;
	private RandomAccessFile reportFile;

	public void setOrder(int order) {
		this.order = order;
	}

	public void setMaxLazyMills(long maxLazyMills) {
		this.maxLazyMills = maxLazyMills;
	}

	public void setReportInterval(long reportInterval) {
		this.reportInterval = reportInterval;
	}

	private void reset(){
		this.serverId=null;
	}

	private File getRptFile(){
		return new File(SinotnRuntime.getBinFolder(), "ReportFace.dt");
	}

	private void getReportTime(){
		try{
			if(this.reportFile==null){
				File file=getRptFile();
				if(file.exists()){
					this.reportFile=new RandomAccessFile(file, "rw");
				}
			}
			if(this.reportFile!=null){
				this.reportTime=new Date(this.reportFile.readLong());
			}
		}catch(Throwable e){
			SinotnLogger.DEBUG.error("读取最近成功上报时间发生系统错误", e);
		}
	}

	private void writeLastRptTime(long timestamp){
		try{
			if(this.reportFile==null){
				File file=getRptFile();
				this.reportFile=new RandomAccessFile(file, "rw");
			}
			this.reportFile.seek(0);
			this.reportFile.writeLong(timestamp);
		}catch(Throwable e){
			SinotnLogger.DEBUG.error("保存最近成功上报时间发生系统错误", e);
		}
	}

	@Override
	public int getOrder() {
		return this.order;
	}

	@Override
	public boolean onExamineeLoad(Object evt) {
		if(examEngine.getRunState()==ExamEngine.STATE_STD){
			// 正式考试状态才启用
			if(!this.start){
				Thread thread=new Thread(this);
				thread.setDaemon(true);
				thread.setName("ExamSignRpt");
				thread.start();
				this.start=true;
			}
		}
		return false;
	}

	@Override
	public boolean onRoundEnd(Object evt) {
		this.start=false;
		this.serverId=null;
		this.reportTime=null;
		if(this.reportFile!=null){
			IOUtils.closeQuietly(this.reportFile);
			this.reportFile=null;
		}
		return false;
	}

	private void lazy(){
		// 发生一次错误，上报时间间隔增加10秒钟
		this.lazyMills+=10000;
		if(this.lazyMills>this.maxLazyMills){
			this.lazyMills=this.maxLazyMills;
		}
	}

	private void connect(){
		if(this.client==null || !this.client.isConnected()){
			Message message;
			while(true){
				String ipAddr=this.ksPlatform.getIpAddr();
				Integer port=this.ksPlatform.getPort();
				if(StringUtils.isNotEmpty(ipAddr) && port!=null){
					try{
						this.client=this.ksPlatform.buidClient(ipAddr, port+1, false);
						this.client.setKeepAlive(true);
						this.client.connect(true);
						//注册终端
						if(this.serverId==null){
							this.serverId=this.serverAuthSvr.getServerAuth().getServerId();
						}
						HashMap<String, Object> params=new HashMap<String, Object>(1);
						params.put("regKey", this.serverId);
						SimpleMessage sm=new SimpleMessage(101, this.client.nextToken());
						sm.setText(GsonFacade.toJson(params));
						MessageNotifier notifier=this.client.send(sm);
						notifier.doWait(0, "平台终端注册");
						message=notifier.getResponse();
						if(message==null || message.getType()!=0){
							throw new RuntimeException("平台终端注册失败["+(message==null?"NULL":String.valueOf(message.getToken()))+"]");
						}
						break;
					}catch(Throwable e){
						this.errCount++;
						SinotnLogger.DEBUG.error("创建服务平台连接失败[ipAddr="+ipAddr+";port="+(port+1)+"]", e);
						if(this.client!=null){
							this.client.disconnect();
							this.client=null;
						}
					}
				}
				if(this.errCount>=100){
					break;
				}else{
					try{
						Thread.sleep(60000);
					}catch(Throwable e){}
				}
			}
		}
	}

	@Override
	public void run() {
		this.reset();
		this.getReportTime();
		MessageNotifier notifier;
		long lastTimestamp;
		int i;
		int size;
		ExamCertifierVO certifier;
		Message message;
		SimpleMessage sm;
		HashMap<String, Object> params;
		final byte faceType=2;
		final byte manualType=0;
		String certifyText;
		int index;
		String examineeId;
		Examinee examinee;
		File photo;
		File folder;
		int sendType;
		String photoPrecision;
		String compScore;
		while(true){
			try{
				Thread.sleep(this.reportInterval+this.lazyMills);
				lastTimestamp=System.currentTimeMillis();
				ExamCertifierCond cond=new ExamCertifierCond();
				cond.setGtCertifyTime(this.reportTime);
				cond.setLeCertifyTime(new Date(lastTimestamp));
				List<ExamCertifierVO> list=runtimeSvr.findExamCertifiers(1, 0, cond);
				if(list!=null && list.size()>0){
					this.connect();
					size=list.size();
					for(i=0;i<size;i++){
						certifier=list.get(i);
						certifyText=certifier.getCertifyText();
						if(certifyText==null) continue;
						index=certifyText.indexOf(":");
						if(index==-1) continue;
						examineeId=certifyText.substring(0, index);
						examinee=this.examEngine.getExaminee(examineeId);
						if(examinee==null) continue;
						folder=FileStoreUtil.getPhotoHttpFolder(examinee.getRoomId(), false);
						if(folder==null) continue;
						photo=new File(folder, FileStoreUtil.getPhotoName(examinee.getId()));
						if(!photo.exists()) continue;
						params=new HashMap<String, Object>();
						if(certifier.getCertifyType()==manualType){
							// 手工签到
							sendType=306;
							params.put("remark", certifyText.substring(index+1));
							params.put("manualName", certifier.getTeacherName());
						}else if(certifier.getCertifyType()==faceType){
							// 人脸比对签到
							sendType=305;
							certifyText=certifyText.substring(index+1);
							index=certifyText.indexOf("|");
							if(index==-1){
								compScore="0";
								photoPrecision="0";
							}else{
								compScore=certifyText.substring(0, index);
								photoPrecision=certifyText.substring(index+1);
							}
							// 比对阀值
							params.put("photoPrecision", photoPrecision);
							// 实际比对值
							params.put("compScore", compScore);
						}else{
							continue;
						}
						sm=new SimpleMessage(sendType,this.client.nextToken());
						params.put("serverId", this.serverId);// 注册服务器Id
						params.put("imeiNo", this.serverId); // 设备号与注册服务器号一致
						params.put("examineeId", examineeId);
						params.put("verifyTime", String.valueOf(certifier.getCertifyTime().getTime()));// 校验时间，字符串时间戳
						//身份证有效期 "faceIndetityDate":"20190816-20290816",
						// 场次开始时间
						params.put("roundBeginTime", String.valueOf(examinee.getBeginTime()));
						// 场次结束时间
						params.put("roundEndTime", String.valueOf(examinee.getEndTime()));
						// 是否通过，0表示通过；1表示不通过。因为签到记录只有通过才存储，所以固定传0
						params.put("isPass", "0");
						params.put("faceType", "1");
						params.put("passType", "0");
						sm.setText(GsonFacade.toJson(params));
						sm.setMessageFiles(new MessageFile[]{
								new DefaultMessageFile(examinee.getCertId()+"@p.jpg", photo)
						});
						// 图片文件
						notifier=this.client.send(sm);
						notifier.doWait(0, "发送身份签到数据");
						message=notifier.getResponse();
						if(message==null || message.getType()!=0){
							throw new MessageException("发送身份签到数据失败["+(message==null?"NULL":String.valueOf(message.getToken()))+"]");
						}
					}
				}
				this.writeLastRptTime(lastTimestamp);
				this.reportTime=new Date(lastTimestamp);
				this.errCount=0;
				this.lazyMills=0;
			}catch(Throwable e){
				this.lazy();
				this.errCount++;
				if(this.errCount>100){
					// 上报异常超过100次终止上报
					this.start=false;
				}
				if(e instanceof MessageException){
					SinotnLogger.DEBUG.error(e.getMessage(), e);
				}else{
					SinotnLogger.DEBUG.error("上报考生签到数据发生系统错误", e);
				}
			}
			if(this.start==false){
				break;
			}
		}
	}
}
